﻿using System;
using System.Drawing;
using System.Windows.Forms;
using GridRayTracer;
using GridRayTracer.Primitives;
using Alchemi.Core.Owner;
using MathNet.Numerics.LinearAlgebra;

namespace GridRayTracerExec
{
  public partial class MainForm : Form
  {
    public MainForm()
    {
      InitializeComponent();            
    }
    
    private void createScene()
    {
      scene = new Scene();
      
      var sphere1 = new Sphere(){ Translation = Vector.Create(new double[]{ -2, 0, 4 }) };
      sphere1.Surface.DiffuseColor = Color.FromArgb(20, 10, 10);
      scene.Add(sphere1);
      
      var sphere2 = new Sphere(){ Translation = Vector.Create(new double[]{ 2, 0, 4 }) };
      sphere2.Surface.DiffuseColor = Color.FromArgb(10, 10, 20);
      scene.Add(sphere2);      
      
      var light = new SpotLight()
      { 
        Location = Vector.Create(new double[]{ 0, -5, 3 }), 
        Color = Color.Green 
      };
      scene.Add(light);
      
      rayTracer = new RayTracer(scene)
      {
        ScreenWidth = image.Width, 
        ScreenHeight = image.Height 
      };      
    }

    private void localRayTracing()
    {
      createScene();
      Rectangle roi = new Rectangle(0, 0, image.Width, image.Height);
      GridRayTracerThread thread = new GridRayTracerThread(roi, rayTracer);            

      try
      {
        thread.Start();
      }
      catch (Exception e)
      {
        OnThreadFailed(thread, e);
        return;
      }

      OnThreadFinish(thread);
      OnApplicationFinish();
    }

    private void gridRayTracing()
    {
      createScene();
      using (GConnectionDialog gconDlg = new GConnectionDialog())
      {
        if (gconDlg.ShowDialog() == DialogResult.OK)
        {
          using (GApplication gapp = new GApplication(gconDlg.Connection))
          {
            gapp.ApplicationName = "GridRayTracer";
            gapp.Manifest.Add(new ModuleDependency(typeof(GridRayTracerThread).Module));
            gapp.Manifest.Add(new ModuleDependency(typeof(Matrix).Module));

            gapp.ThreadFinish += new GThreadFinish(OnThreadFinish);
            gapp.ApplicationFinish += new GApplicationFinish(OnApplicationFinish);
            gapp.ThreadFailed += new GThreadFailed(OnThreadFailed);
            
            int threadWidth = (int)threadWidthUD.Value;
            int threadHeight = (int)threadHeightUD.Value;
            for (int y = 0; y < image.Height; y += threadHeight)
            {
              for (int x = 0; x < image.Width; x += threadWidth)
              {
                Rectangle roi = new Rectangle(x, y,
                  Math.Min(threadWidth, image.Width - x),
                  Math.Min(threadHeight, image.Height - y));

                GridRayTracerThread thread = new GridRayTracerThread(roi, rayTracer);

                gapp.Threads.Add(thread);
              }
            }

            gapp.Start();
          }
        }
      }
    }

    private void OnThreadFinish(GThread thread)
    {
      GridRayTracerThread gt = (GridRayTracerThread)thread;
      Graphics g = Graphics.FromImage(image);
      g.DrawImage(gt.Image, gt.Roi);
      logBox.Text += "Thread [" + gt.Roi.ToString() + "] finished\n";
    }

    private void OnApplicationFinish()
    {
      imagePb.Image = image;
      logBox.Text += "Grid application finished\n";
    }

    private void OnThreadFailed(GThread thread, Exception e)
    {
      MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
      logBox.Text += "Error: " + e.Message + "\n";
    }

    private void startBtn_Click(object sender, EventArgs e)
    {
      image = new Bitmap((int)widthUD.Value, (int)heightUD.Value);
      imagePb.Width = (int)widthUD.Value;
      imagePb.Height = (int)heightUD.Value;
      if (localRB.Checked)
        bgWorker.RunWorkerAsync();
      else
        gridRayTracing();
    }

    private void gridRB_CheckedChanged(object sender, EventArgs e)
    {
      partForThreadLbl.Enabled = partForThread2Lbl.Enabled
        = threadHeightUD.Enabled = threadWidthUD.Enabled = gridRB.Checked;
    }

    private void bgWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
    {
      localRayTracing();
    }

    private Bitmap image = null;
    private Scene scene = null;
    private RayTracer rayTracer = null;
  }
}
